/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024.
 * Description: RTOS Process Context asm header
 * Create: 2024-07-13
 */
#ifndef __ASM_RTOS_RTPC_H
#define __ASM_RTOS_RTPC_H

#include <linux/types.h>
#include <linux/sched/task_stack.h>
#include <linux/context_tracking.h>
#include <linux/percpu-defs.h>
#include <linux/mmu_context.h>

#include <asm/tls.h>
#include <asm/cp15.h>

#define rtpc_set_current(task)

DECLARE_PER_CPU(bool, rtpc_mm_fast_switch);

void rtpc_thread_wake_code(void);
static __always_inline
void rtpc_tls_thread_switch(struct task_struct *prev, struct task_struct *next);

static inline void rtpc_user_exit(void)
{
#ifdef CONFIG_ALIGNMENT_TRAP
	if (get_cr() != cr_alignment)
		set_cr(cr_alignment);
#endif

	context_tracking_user_exit();
}

static inline void rtpc_user_enter(const bool is_wait_call)
{
	context_tracking_user_enter();
}

static __always_inline void rpc_create_context(struct task_struct *const tsk)
{
	task_thread_info(tsk)->cpu_context.sp = (uintptr_t)task_pt_regs(tsk);
	task_thread_info(tsk)->cpu_context.pc = (uintptr_t)rtpc_thread_wake_code;
}

static __always_inline
void rtpc_context_switch_user(struct task_struct *prev, struct task_struct *next)
{
	raw_cpu_write(rtpc_mm_fast_switch, true);
	/* help the compiler optimizes the code */
	if (unlikely(prev->active_mm == next->mm))
		__builtin_unreachable();
	switch_mm_irqs_off(prev->active_mm, next->mm, next);
	raw_cpu_write(rtpc_mm_fast_switch, false);
	rtpc_tls_thread_switch(prev, next);
}

static __always_inline
void rtpc_context_switch_kernel(struct task_struct *prev, struct task_struct *next)
{
}

static __always_inline
void rtpc_tls_thread_switch(struct task_struct *prev, struct task_struct *next)
{
	struct thread_info *prev_thread_info = task_thread_info(prev);
	struct thread_info *next_thread_info = task_thread_info(next);

	asm(
			"mrc p15, 0, %0, c13, c0, 2\n\t"
			"mcr p15, 0, %1, c13, c0, 3\n\t"
			"mcr p15, 0, %2, c13, c0, 2"
			: "=&r"(prev_thread_info->tp_value[1])
			: "r"(next_thread_info->tp_value[0]), "r"(next_thread_info->tp_value[1])
			:);
}
#endif
